package cn.tips.dmp.supports.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.*;

@Slf4j
@Component
public class JwtUtil {
    static final String SECRET = "ThisIsTipsASecret";
    public static final String TOKEN_PREFIX = "Tips_";
    public static final String JWT_SHIRO_CACHE_KEY_PREFIX = "tips-access-token";
    // 过期时间 1 周
    private static final long EXPIRE_TIME = 7* 24 * 60 * 60 * 1000;
    // 过期时间 1 个月
    public static final long MEMBER_EXPIRE_TIME = 30* 24 * 60 * 60;
    //一般redis过期时间 60分钟
    public static final long REDIS_EXPIRE_TIME = 60 * 60;

    /**
     * 生成token，1个小时候失效
     *
     * @param phoneNumber  手机号
     * @param openId  openId
     * @param unionId  unionId
     * @return 加密的token
     */
    public static String generateToken(String phoneNumber , String openId , String unionId , String loginType) throws UnsupportedEncodingException {
        long startTime;
        long E_time;
        Object obj;
        // 获取开始时间
        startTime = System.currentTimeMillis();

        HashMap<String, Object> map = new HashMap<>();
        map.put("phoneNumber", phoneNumber);
        map.put("openId", openId);
        map.put("unionId", unionId);
        map.put("loginType", loginType);

        JWTCreator.Builder builder = JWT.create();
        for (String s : map.keySet()) {
            builder.withClaim(s, map.get(s)+"");
        }
        String jwt = builder.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRE_TIME))
                .sign(Algorithm.HMAC256(SECRET));

        return TOKEN_PREFIX + jwt; //jwt前面一般都会加Bearer
    }


    /**
     * 校验 token 是否正确
     *
     * @param token    密钥
     * @param phoneNumber 手机号
     * @return 是否正确
     */
    public static boolean verify(String token, String phoneNumber) {
        try {
            Algorithm algorithm = Algorithm.HMAC512(SECRET);
            //在token中附带了username信息
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("phoneNumber", phoneNumber)
                    .build();
            //验证 token
            DecodedJWT _de_jwt = verifier.verify(token.replace(TOKEN_PREFIX,""));

            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * 获得token中的信息，无需secret解密也能获得
     *
     * @param token 密钥
     * @return token中包含的用户名
     */
    public static String getPhoneNumber(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token.replace(TOKEN_PREFIX,""));
            return jwt.getClaim("phoneNumber").asString();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获得token中的信息，无需secret解密也能获得
     *
     * @param token 密钥
     * @return token中包含的用户名
     */
    public static String getLoginType(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token.replace(TOKEN_PREFIX,""));
            return jwt.getClaim("loginType").asString();
        } catch (Exception e) {
            return null;
        }
    }


    public static HttpServletRequest validateTokenAndAddUserIdToHeader(HttpServletRequest request) {
        String token = request.getHeader(JWT_SHIRO_CACHE_KEY_PREFIX);
        if (token != null) {
            // parse the token.
            try {
                Map body = JWT.decode(token.replace(TOKEN_PREFIX,"")).getClaims();

                return new CustomHttpServletRequest(request, body);
            } catch (Exception e) {
                log.info(e.getMessage());
                throw new TokenValidationException(e.getMessage());
            }
        } else {
            throw new TokenValidationException("Missing token");
        }
    }


    public static class CustomHttpServletRequest extends HttpServletRequestWrapper {
        private Map<String, String> claims;

        public CustomHttpServletRequest(HttpServletRequest request, Map<String, ?> claims) {
            super(request);
            this.claims = new HashMap<>();
            claims.forEach((k, v) -> this.claims.put(k, String.valueOf(v)));
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            if (claims != null && claims.containsKey(name)) {
                return Collections.enumeration(Arrays.asList(claims.get(name)));
            }
            return super.getHeaders(name);
        }

        public Map<String, String> getClaims() {
            return claims;
        }
    }

    static class TokenValidationException extends RuntimeException {
        public TokenValidationException(String msg) {
            super(msg);
        }
    }

    /**
     * 使用不同的算法对字符串加密
     *
     * @param password
     * @param algorithm
     * @return String
     */
    public static String encode(String password, String algorithm)
    {
        byte[] unencodedPassword = password.getBytes();
        MessageDigest md = null;
        try
        {
            md = MessageDigest.getInstance(algorithm);
        }
        catch (Exception e)
        {
            return password;
        }
        md.reset();
        md.update(unencodedPassword);
        byte[] encodedPassword = md.digest();
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < encodedPassword.length; i++)
        {
            if ((encodedPassword[i] & 0xff) < 0x10)
            {
                buf.append("0");
            }
            buf.append(Long.toString(encodedPassword[i] & 0xff, 16));
        }
        return buf.toString();
    }


    /**
     * 获得token中的信息，无需secret解密也能获得
     *
     * @param token 密钥
     * @param str (userName,userId,loginId)
     * @return token中包含的用户名
     */
    public static String getUser(String token, String str) {
        try {
            DecodedJWT jwt = JWT.decode(token.replace(TOKEN_PREFIX,""));
            return jwt.getClaim(str).asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }


    public static void main(String[] args) throws UnsupportedEncodingException{
        String str = generateToken("admin","admin","admin",null);
        String token = "Bestbond "+str;//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJzZWN1cmUtbG9naW4iLCJsb2dpblR5cGUiOiIxIiwiZXhwIjoxNTQ2NTk4NjkxLCJ1c2VybmFtZSI6ImFkbWluIn0.Pelq3qbejzAH6VJHK5VWPaFx6bA8oZS1NJ6XgA76h-OVZM552bCWTyVNm9j9jVFa6yq4dbXJgTqJvGYFnNFW6Q";

        Map body = JWT.decode(token.replace(TOKEN_PREFIX,"")).getClaims();
        for (Object o : body.keySet()) {
            System.out.println(o);
        }

        verify(token, "admin");
        verifyLoginType(token, "loginType");
        System.out.println(getPhoneNumber(token));
        System.out.println(getLoginType(token));
    }

    /**
     * 校验 token 是否正确
     *
     * @param token    密钥
     * @param loginType 用户名
     * @return 是否正确
     */
    public static boolean verifyLoginType(String token, String loginType) {
        try {
            Algorithm algorithm = Algorithm.HMAC512(SECRET);
            //在token中附带了loginType信息
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("loginType", loginType)
                    .build();
            //验证 token
            DecodedJWT _de_jwt = verifier.verify(token.replace(TOKEN_PREFIX,""));

            return true;
        } catch (Exception exception) {
            return false;
        }
    }
}
